package client;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;

import util.Logger;
import util.SerializationHelper;
import dataContracts.FileStatus;
import dataContracts.InstallSettings;
import dataContracts.SharedDirectory;
import dataContracts.SharedFile;

public class ClientDirectory extends SharedDirectory {
	private static final long serialVersionUID = -6891747537273045771L;
	transient SerializationHelper<ClientDirectory> _serializer;
	String settingsFileName = ".lanyard";
	File settingsFile;
	public InstallSettings config; 
	
	public ClientDirectory(String directoryPath) {
		super(directoryPath);
		
		config = InstallSettings.getSettings();
		
		_serializer = new SerializationHelper<ClientDirectory>();
		settingsFile = new File(this.getFilePath() + File.separator + settingsFileName);
		
		if (IsConfigured()) {
			ClientDirectory old = this.Deserialize();
			this.files = old.files;
			this.directoryInfo = old.directoryInfo;
			this.directoryInfo.files = old.directoryInfo.files;
			initialize();
			System.out.println("loaded from config object");
		} else {
			Install();
			System.out.println("loaded from scratch");
		}
		
	}
	
	// check for already configured directory...
	public boolean IsConfigured() {
		return settingsFile.exists() && (_serializer.Deserialize(settingsFile) != null);
	}
	
	public void refresh() {
		super.refresh();
		
		// check for new files
		if (directory.isDirectory()) {
	    	for (File f : directory.listFiles()) {
	    		if (!f.isHidden() && !f.getName().equals(".lanyard")) {
	    			SharedFile cf = findFile(f.getPath());
	    			if (cf == null) {
	    				this.addFile(new SharedFile(f));
	    			}
	    		}
			}
		}
	}
	
	public void initialize() {
		if (directory.isDirectory()) {
	    	for (File f : directory.listFiles()) {
	    		if (!f.isHidden() && !f.getName().equals(".lanyard")) {
	    			SharedFile cf = findFile(f.getPath());
	    			if (cf == null) {
	    				this.addFile(new SharedFile(f));
	    			}
	    		}
			}
		}
		
    	// must store removed files to delete afterward to avoid ConcurrentModificationExceptions
    	ArrayList<SharedFile> toDelete = new ArrayList<SharedFile>();
    	for (SharedFile sf : files) {
    		if (!sf.getFileReference().exists()) {
    			toDelete.add(sf);
    		}
		}
    	// remove the files that are no longer on the disk
    	for (SharedFile sf : toDelete) {
    		removeFile(sf);
		}
    	
    	refresh();
	}
	
	public void Install() {
		// load from scratch
		files.clear();
		
		if (directory.isDirectory()) {
	    	for (File f : directory.listFiles()) {
	    		if (!f.isHidden() && !f.getName().equals(".lanyard")) {
	    			SharedFile cf = new SharedFile(f);
	    			cf.refresh();
	    			this.files.add(cf);
	    		}
			}
    	}
		
		updateDirectoryChecksum();
		
		// save state for future use
		SaveChanges();
	}
	
	public void UnInstall() {
		settingsFile.delete();
	}
	
	public boolean hasLocalChanges() {
		for (SharedFile f : this.files) {
			if (f.status != FileStatus.Synchronized)
				return true;
		}
		
		return false;
	}
	
	// METHODS FOR ADDING FILES TO THE COLLECTION 
	
	public void addFile(SharedFile file) {
		file.status = FileStatus.Added;
		file.fileState.owner = config.userName;
		files.add(file);
		
		SaveChanges();
	}
	
	public void addFile(String fileName) throws FileNotFoundException {
		addFile(findOrCreateFile(fileName));
	}
	
	// METHODS FOR REMOVING A FILE FROM THE DIRECTORY/COLLECTION
	public void removeFile(String path) {
		SharedFile f = findFile(path);
		if (f == null) {
			return;
		}
		
		f.status = FileStatus.Deleted;
		removeFile(f);
	}
	
	public void removeFile(SharedFile sf) {
		
		if (sf != null) {
			files.remove(sf);
		}
		
		sf.getFileReference().delete();
		
		SaveChanges();
	}
	
	//SOME OTHER STUFF -----------------------
	
	public SharedFile findFile(String path) {
		for (SharedFile f : this.files) {
			if (f.getFullPath().equals(path)) {
				return f;
			}
			
			// some older uses just pass the file name... this should still work
			if (f.getName().equals(path)) {
				return f;
			}
		}
		
		return null;
	}
	
	public SharedFile findOrCreateFile(String path) throws FileNotFoundException {
		SharedFile cf = findFile(path);
		
		if (cf == null) {
			File f = new File(config.directoryPath + File.separator + path);
			if (!f.exists())
				throw new FileNotFoundException();
			
			cf = new SharedFile(f);
		}
		
		return cf;
	}
	
	public void SaveChanges() {
		this.Serialize();
	}
	
	public void Serialize() {
		FileOutputStream fs;
		try {
			fs = new FileOutputStream(settingsFile);
			_serializer.Serialize(this, fs);
		} catch (FileNotFoundException e) { e.printStackTrace(); }
	}
	
	public void Serialize(OutputStream stream) {
		_serializer.Serialize(this, stream);
	}
	
	public ClientDirectory Deserialize() {
		ClientDirectory old = (ClientDirectory) _serializer.Deserialize(settingsFile);
		if (old != null) {
			this.files = old.files;
			this.directoryInfo = old.directoryInfo;
			this.directoryInfo.files = old.directoryInfo.files;
			
			// restore the file references
			for (SharedFile file : this.files) {
				file.afterDeserialize();
			}
		}
		
		return old;
	}
}
